package service

import (
	"bufio"
	"context"
	"fmt"
	"github.com/gorilla/websocket"
	"io"
	"log"
	"net/http"
	"os/exec"
	"sync"
	"syscall"
	"time"
)

type Message struct {
	client *Client
	msg    []byte
}

type Client struct {
	sync.Mutex
	Conn      *websocket.Conn
	readChan  chan []byte
	writeChan chan []byte
	closeChan chan struct{}
	closed    bool
	Pid       int
}

func NewClient(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Client, error) {
	// 升级get请求为websocket协议
	ws, err := upgrader.Upgrade(w, r, responseHeader)
	if err != nil {
		log.Println("New Client err:", err)
		return nil, err
	}
	client := &Client{
		Conn:      ws,
		readChan:  make(chan []byte, 5),
		writeChan: make(chan []byte, 5),
		closeChan: make(chan struct{}),
	}
	go client.readLoop()
	go client.writeLoop()
	return client, nil
}
func (c *Client) writeLoop() {
	for {
		select {
		case msg := <-c.writeChan:
			err := c.Conn.WriteMessage(websocket.TextMessage, msg)
			if err != nil {
				c.Close()
				return
			}
		case <-c.closeChan:
			return
		}
	}
}

func (c *Client) readLoop() {
	for {
		_, msg, err := c.Conn.ReadMessage()
		//	fmt.Println("----readLoop----",msg,"  err:",err)
		if err != nil {
			c.Close()
			fmt.Println("log kill pid---->", c.Pid)
			err = syscall.Kill(c.Pid, syscall.SIGKILL)
			if err != nil {
				log.Println(err.Error())
				return
				//panic(err)
			}
			return
		}
		select {
		case c.readChan <- msg:
		case <-c.closeChan:
			return
		}
	}
}

//func (c *Client) ReadMessage() ([]byte, bool) {
//	select {
//	case <-time.After(time.Minute*5 + time.Second*10):
//		c.Close()
//		return nil, false
//	case data := <-c.readChan:
//		return data, true
//	case <-c.closeChan:
//		return nil, false
//	}
//}

func (c *Client) WriteMessage(data []byte) {
	select {
	case <-c.closeChan:
	case c.writeChan <- data:
	case <-time.After(time.Second * 3):
		c.Close()
	}
}

//Write(p []byte) (n int, err error)
//func (kl *Client) Write(data []byte) (int, error) {
//	if err := kl.Conn.WriteMessage(websocket.TextMessage, data); err != nil {
//		return 0, err
//	}
//	return len(data), nil
//}

func (c *Client) Close() error {
	c.Lock()
	defer c.Unlock()
	if !c.closed {
		close(c.closeChan)
		c.closed = true
	}
	return c.Conn.Close()
}

// --- // read log from log file----
func ReadLogs(ctx context.Context, logfile string, client *Client) {

	c := exec.CommandContext(ctx, "bash", "-c", "tail -f  "+logfile)
	stdout, _ := c.StdoutPipe()
	stderr, _ := c.StderrPipe()

	var wg sync.WaitGroup
	// 因为有2个任务, 一个需要读取stderr 另一个需要读取stdout
	wg.Add(2)
	go readLogFile(ctx, &wg, stderr, client)
	go readLogFile(ctx, &wg, stdout, client)
	// 这里一定要用start,而不是run 详情请看下面的图
	err := c.Start()
	fmt.Println("PID-->", c.Process.Pid)
	client.Pid = c.Process.Pid // 用于kill改进程
	proState, err := c.Process.Wait()
	if err != nil {
		panic(err)
	}
	fmt.Println("waiting status-->", proState.String(), client.Pid)
	// 等待任务结束
	wg.Wait()
	fmt.Println("----END------")
}

func readLogFile(ctx context.Context, wg *sync.WaitGroup, std io.ReadCloser, client *Client) {
	reader := bufio.NewReader(std)
	defer wg.Done()
	for {
		select {
		case <-ctx.Done():
			return
		default:
			readString, err := reader.ReadString('\n')

			if err != nil || err == io.EOF {
				return
			}
			fmt.Print(readString)
			client.WriteMessage([]byte(readString))
		}
	}
}
